/*
Title:  CPU Stub
Author: ChinniKrishna Kothapalli
Description: Generates CPU transacations from Trace file generated randomly
Created for: ECE510: System Verilog Final Project
*/
module CpuStub(BusInterface.CPUPort CpuBus);
	timeunit 1ns;
	timeprecision 1ns;
	//Imports all the contents of the package
	import BamPackage::*;
	//Signals
	reg Clock;
	reg Reset;
	//Local Variable
	integer File,Read;									//File Pointer and Read line
	integer Operation,Address,Data,WaitCycles;			//Contents of each line
	//Getting Data from the file
	initial
	begin
		//Open File
		File=$fopen("Trace.txt","r");					
		if (!File)									    //If not found
		begin
		    $display("Could not open \"Trace.txt\"");	//Display error message
		    $finish;								 	//End the simulation
		end
		else											//If found
		begin
			$display("Trace.txt Opened Sucessfully");  //Display success message
   		    //Iterate till the end of the file
			while (!$feof(File))					  
			begin
				Read=$fscanf(File,"%s %h %h %d",Operation,Address,Data,WaitCycles);
				//Depending on operation call the respective task
				case(Operation)
					"RST":
						Rst;						
					"MEMR":
						MemRead;
					"MEMW":
						MemWrite;
					"IOR":
						IORead;
					"IOW":
						IOWrite;
				endcase				
			end//End of While
			$finish();
		end//End of Else				
	end//End of Block
	
	initial
	begin
		integer outfile;
		outfile = $fopen ("TestOutput.txt", "w");
		//Monitors
		$fmonitor(outfile,$time,"Clock=%b\tAddrData=%h\tAddrStatus=%h\tReady=%b\tALE=%b\tIOM=%b\tRD=%b\tWR=%b\t",CpuBus.Clock,CpuBus.AddrData,CpuBus.AddrStatus,CpuBus.Ready,CpuBus.ALE,CpuBus.IOM,CpuBus.RD,CpuBus.WR);
		
	end
	
	//System Wide Clock
	initial 
	begin
		Clock=Low;
		assign CpuBus.Clock=Clock;
		forever #ClockWidth Clock = ~(Clock);
	end
	
	//Tasks
	//Reset Task
	task Rst;
	begin
		@(negedge CpuBus.Clock)
		begin
			$display("Reset");
			CpuBus.Reset=High;
			CpuBus.AddrStatus='z;
			CpuBus.AddrData='z;
			repeat (IdleClocks) @ (negedge Clock);
			CpuBus.Reset=Low;
		end
	end
	endtask
	
	//Memory Read Task
	task MemRead;
	begin
		//Cycle T1 of Memory Read
		@(negedge CpuBus.Clock)
		begin
			CpuBus.Ready=High;																
			{CpuBus.AddrStatus,CpuBus.AddrData}=Address;//Output a 20bit memory address
			CpuBus.ALE=High;							//Enabling Address latch
			CpuBus.IOM=High;							//Indicate a memory access			
			CpuBus.RD=High;								//RD is high
			CpuBus.WR=High;								//WR is high
		end	
		//Cycle T2 of Memory Read
		@(negedge CpuBus.Clock)
		begin
			//If we get the data immediately assert Ready
			if(WaitCycles==0)
				CpuBus.Ready=High;
			//Else make ready low
			if(WaitCycles!=0)
				CpuBus.Ready=Low;
			CpuBus.AddrData='z;   						//Tristate the Address.
			CpuBus.AddrStatus=4'b1100;					//Set Status Bits
			CpuBus.ALE=Low;								//Disabling the Address latch
			CpuBus.IOM=High;							//Indicate a memory access			
			CpuBus.RD=High;								//RD is high
			CpuBus.WR=High;								//WR is high			
		end
		
		//Cycle T3 of Memory Read
		@(negedge CpuBus.Clock)
		begin
			//Introduce some wait states until data is received
			if(WaitCycles!=0)
			begin
				repeat(WaitCycles)
				begin
					@(negedge CpuBus.Clock)
					begin
						CpuBus.Ready=Low;				//Data is not ready
						CpuBus.AddrData='z;   			//Tristate the Address.
						CpuBus.AddrStatus=4'b1100;		//Send Status Bits
						CpuBus.ALE=Low;					//Disabling the Address latch
						CpuBus.IOM=High;				//Indicate a memory access			
						CpuBus.RD=Low;					//Indicate a Read Operation
						CpuBus.WR=High;					//WR is high	
					end								
				end
				CpuBus.AddrData=Data;					//Data after waiting		
			end
			//Else give data 
			CpuBus.Ready=High;							//Data is ready
			CpuBus.AddrData=Data;  						//Tristate the Address.
			CpuBus.AddrStatus=4'b1100;					//Send Status Bits
			CpuBus.ALE=Low;								//Disabling the Address latch
			CpuBus.IOM=High;							//Indicate a memory access			
			CpuBus.RD=Low;								//Indicate a Read Operation
			CpuBus.WR=High;								//WR is high				
		end
		//Cycle T4 of Memory Read
		@(negedge CpuBus.Clock)
		begin
			CpuBus.Ready=High;								
			CpuBus.AddrData='z;  						//Tristate the Address.
			CpuBus.AddrStatus=4'b1100;					//Status Bits
			CpuBus.ALE=Low;								//Disabling the Address latch
			CpuBus.IOM=High;							//Indicate a memory access			
			CpuBus.RD=High;								//RD is high
			CpuBus.WR=High;								//WR is high	
		end
	end
	endtask
	
	//Memory Write 
	task MemWrite;
	begin
		//Cycle T1 of Memory Write
		@(negedge CpuBus.Clock)
		begin
			CpuBus.Ready=High;																
			{CpuBus.AddrStatus,CpuBus.AddrData}=Address;//Output a 20bit memory address
			CpuBus.ALE=High;							//Enabling Address latch
			CpuBus.IOM=High;							//Indicate a memory access			
			CpuBus.RD=High;								//RD is high
			CpuBus.WR=High;								//WR is high
		end	
		//Cycle T2 of Memory Write
		@(negedge CpuBus.Clock)
		begin
			//If we get the data immediately assert Ready
			if(WaitCycles==0)
				CpuBus.Ready=High;
			//Else make ready low
			if(WaitCycles!=0)
				CpuBus.Ready=Low;
			CpuBus.AddrData='z;   						//Tristate the Address.
			CpuBus.AddrStatus=4'b1100;					//Send Status Bits
			CpuBus.ALE=Low;								//Disabling the Address latch
			CpuBus.IOM=High;							//Indicate a memory access			
			CpuBus.RD=High;								//RD is high
			CpuBus.WR=High;								//WR is high			
		end		
		
		//Cycle T3 of Memory Write
		@(negedge CpuBus.Clock)
		begin
			if(WaitCycles!=0)
			begin
				repeat(WaitCycles)
				begin
					@(negedge CpuBus.Clock)
					begin
						CpuBus.Ready=Low;				//Data is not ready
						CpuBus.AddrData='z;   			//Tristate the Address.
						CpuBus.AddrStatus=4'b1100;		//Send Status Bits
						CpuBus.ALE=Low;					//Disabling the Address latch
						CpuBus.IOM=High;				//Indicate a memory access			
						CpuBus.RD=High;					//RD is high
						CpuBus.WR=Low;					//Indicate a Write Operation	
					end
				end
				CpuBus.AddrData=Data;					//Data after waiting		
			end
			CpuBus.Ready=High;							//Data is not ready
			CpuBus.AddrData=Data;  						//Tristate the Address.
			CpuBus.AddrStatus=4'b1100;					//Send Status Bits
			CpuBus.ALE=Low;								//Disabling the Address latch
			CpuBus.IOM=High;							//Indicate a memory access			
			CpuBus.RD=High;								//RD is high
			CpuBus.WR=Low;								//WR is high				
		end
		//Cycle T4 of Memory Write
		@(negedge CpuBus.Clock)
		begin
			CpuBus.Ready=High;							//Data is not ready
			CpuBus.AddrData='z;  						//Tristate the Address.
			CpuBus.AddrStatus=4'b1100;					//Send Status Bits
			CpuBus.ALE=Low;								//Disabling the Address latch
			CpuBus.IOM=High;							//Indicate a memory access			
			CpuBus.RD=High;								//RD is high
			CpuBus.WR=High;				
		end
	end
	endtask
	
	//IO Read
	task IORead;
	begin
		//Cycle T1 of IO Read
		@(negedge CpuBus.Clock)
		begin
			CpuBus.Ready=High;								
			{CpuBus.AddrStatus,CpuBus.AddrData}=Address;//Output a 20bit memory address
			CpuBus.ALE=High;							//Enabling Address latch
			CpuBus.IOM=Low;								//Indicate an IO access			
			CpuBus.RD=High;								//RD is high
			CpuBus.WR=High;								//WR is high
		end
		//Cycle T2 of IO Read
		@(negedge CpuBus.Clock)
		begin
			if(WaitCycles==0)
				CpuBus.Ready=High;
			if(WaitCycles!=0)
				CpuBus.Ready=Low;
			CpuBus.AddrData='z;   						//Tristate the Address.
			CpuBus.AddrStatus=4'b1100;					//Send Status Bits
			CpuBus.ALE=Low;								//Disabling the Address latch
			CpuBus.IOM=Low;								//Indicate a IO access			
			CpuBus.RD=High;								//RD is high
			CpuBus.WR=High;								//WR is high			
		end
		//Cycle T3 of IO Read
		@(negedge CpuBus.Clock)
		begin
			if(WaitCycles!=0)
			begin
				repeat(WaitCycles)
				begin
					@(negedge CpuBus.Clock)
					begin
						CpuBus.Ready=Low;				//Data is not ready
						CpuBus.AddrData='z;   			//Tristate the Address.
						CpuBus.AddrStatus=4'b1100;		//Send Status Bits
						CpuBus.ALE=Low;					//Disabling the Address latch
						CpuBus.IOM=Low;					//Indicate an IO access			
						CpuBus.RD=Low;					//Indicate a Read
						CpuBus.WR=High;					//WR is high	
					end							
				end
				CpuBus.AddrData=Data;					//Data after waiting		
			end
			CpuBus.Ready=High;							
			CpuBus.AddrData=Data;  						
			CpuBus.AddrStatus=4'b1100;					//Send Status Bits
			CpuBus.ALE=Low;								//Disabling the Address latch
			CpuBus.IOM=Low;								//Indicate an IO access
			CpuBus.RD=Low;								//Indicate a Read
			CpuBus.WR=High;								//WR is high				
		end
		//Activities during T4 Cycle
		@(negedge CpuBus.Clock)
		begin
			CpuBus.Ready=High;							
			CpuBus.AddrData='z;  						//Tristate the Address.
			CpuBus.AddrStatus=4'b1100;					//Send Status Bits
			CpuBus.ALE=Low;								//Disabling the Address latch
			CpuBus.IOM=Low; 							//Indicate an IO access
			CpuBus.RD=High;								//RD is high
			CpuBus.WR=High;				
		end
	end
	endtask
	//IO Write
	task IOWrite;
	begin
		//Cycle T1 of IO Write
		@(negedge CpuBus.Clock)
		begin
			CpuBus.Ready=High;								
			{CpuBus.AddrStatus,CpuBus.AddrData}=Address;//Output a 20bit memory address
			CpuBus.ALE=High;							//Enabling Address latch
			CpuBus.IOM=Low;								//Indicate an IO access
			CpuBus.RD=High;								//RD is high
			CpuBus.WR=High;								//WR is high
		end
		//Cycle T2 of IO Write
		@(negedge CpuBus.Clock)
		begin
			if(WaitCycles==0)
				CpuBus.Ready=High;
			if(WaitCycles!=0)
				CpuBus.Ready=Low;
			CpuBus.AddrData='z;   						//Tristate the Address.
			CpuBus.AddrStatus=32;						//Send Status Bits
			CpuBus.ALE=Low;								//Disabling the Address latch
			CpuBus.IOM=Low;								//Indicate a IO access			
			CpuBus.RD=High;								//RD is high
			CpuBus.WR=High;								//WR is high			
		end
		//Activities during T3 Cycle
		@(negedge CpuBus.Clock)
		begin
			if(WaitCycles!=0)
			begin
				repeat(WaitCycles)
				begin
					@(negedge CpuBus.Clock)
					begin
						CpuBus.Ready=Low;				//Data is not ready
						CpuBus.AddrData='z;   			//Tristate the Address.
						CpuBus.AddrStatus=4'b1100;		//Send Status Bits
						CpuBus.ALE=Low;					//Disabling the Address latch
						CpuBus.IOM=Low;					//Indicate an IO access			
						CpuBus.RD=High;					//RD is high
						CpuBus.WR=Low;					//Indicate a Write Access
					end
					
				end
				CpuBus.AddrData=Data;					//Data after waiting		
			end
			CpuBus.Ready=High;							
			CpuBus.AddrData=Data;  						//Tristate the Address.
			CpuBus.AddrStatus=4'b1100;					//Send Status Bits
			CpuBus.ALE=Low;								//Disabling the Address latch
			CpuBus.IOM=Low;								//Indicate an IO access			
			CpuBus.RD=High;								//RD is high
			CpuBus.WR=Low;								//Indicate a write		
		end
		//Activities during T4 Cycle
		@(negedge CpuBus.Clock)
		begin
			CpuBus.Ready=High;							//Data is not ready
			CpuBus.AddrData='z;  						//Tristate the Address.
			CpuBus.AddrStatus=4'b1100;					//Send Status Bits
			CpuBus.ALE=Low;								//Disabling the Address latch
			CpuBus.IOM=Low;								//Indicate an IO access			
			CpuBus.RD=High;								//RD is high
			CpuBus.WR=High;				
		end
	end
	endtask

	
endmodule
